package com.scsoft.dataacceptor.client;

import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.scsoft.dataacceptor.mongo.MongoResultData;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;

@Component
public class VocDbClient {
	private static Logger log = LoggerFactory.getLogger(VocDbClient.class);
	@Value("${tcp.dbhost}")
	private String host = "27.223.15.42";
	@Value("${tcp.dbport}")
	private int port = 6113;
	@Value("${tcp.run}")
	private boolean run = false;
	@Autowired
	private MongoResultData mongoResultData;
	
	private volatile boolean closed = false;
	private volatile boolean inited = false;
	private volatile Bootstrap bootstrap;
	private volatile EventLoopGroup workerGroup;
	private volatile ChannelFuture channelFuture = null;

	@PostConstruct
	public void init() {
		if (!run || inited)
			return;
		closed = false;
		workerGroup = new NioEventLoopGroup();
		bootstrap = new Bootstrap();
		bootstrap.group(workerGroup).channel(NioSocketChannel.class).option(ChannelOption.SO_KEEPALIVE, true)
				.option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<SocketChannel>() {
					@Override
					public void initChannel(SocketChannel ch) throws Exception {
						ChannelPipeline pipeline = ch.pipeline();
						pipeline.addFirst(new ChannelInboundHandlerAdapter() {
							@Override
							public void channelInactive(ChannelHandlerContext ctx) throws Exception {
								super.channelInactive(ctx);
								ctx.channel().eventLoop().schedule(new Runnable() {
									@Override
									public void run() {
										log.error("NettyTcpClient channel disconnect, try reconnect!");
										doConnect();
									}
								}, 1, TimeUnit.SECONDS);
								// ctx.close();
							}
						});
						pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(800));
						pipeline.addLast(new VocDbClientHandler(mongoResultData));
					}
				});
		doConnect();
		inited = true;
	}

	private void doConnect() {
		if (closed) {
			return;
		}
		channelFuture = bootstrap.connect(this.host, this.port);
		channelFuture.addListener(new ChannelFutureListener() {
			public void operationComplete(ChannelFuture f) throws Exception {
				if (f.isSuccess()) {
					log.info("Started Suma Db Client Success: RemoteHost={}, RemotePort={}.", host, port);
				} else {
					log.error("Started Suma Db Client Failed: RemoteHost={}, RemotePort={}, try restart!", host,
							port);
					f.channel().eventLoop().schedule(new Runnable() {
						@Override
						public void run() {
							doConnect();
						}
					}, 1, TimeUnit.SECONDS);
				}
			}
		});
	}

	public void doTcpRequest(byte[] request) {
		ByteBuf buffer = Unpooled.wrappedBuffer(request);
		Channel channel = channelFuture.channel();
		channel.writeAndFlush(buffer);
	}

	@PreDestroy
	public void close() {
		if (inited) {
			closed = true;
			workerGroup.shutdownGracefully();
			log.info("Stopped Suma Db Client: RemoteHost={}, RemotePort={}", host, port);
		}
	}
}